using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Xml;
using Microsoft.SharePoint;
using System.Diagnostics;
using System.Reflection;
using System.Globalization;
using System.Collections;

namespace CreateDDF
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.ReadLine();
            string fullArgs = string.Empty;
            bool isReleaseMode = false;
            bool includePDB = true;
			bool getsource = false;
			bool useFileReferences = false;
            
            foreach (string item in args)
            {
                fullArgs += item + " ";
            }
            fullArgs = fullArgs.Trim();

            List<string> acceptableParams = new List<string>();
            acceptableParams.Add("n");
            acceptableParams.Add("d");//something I needed for something I was doing
			acceptableParams.Add("m");
			acceptableParams.Add("s");//use included SPSource functionality, don't need to using codeplex.com/spsource
			acceptableParams.Add("r");//use file references
            //acceptableParams.Add("releasemode");
            //acceptableParams.Add("pdb");
            

            string[] newArgs = fullArgs.Split('-');
            Dictionary<string, string> dArgs = new Dictionary<string, string>();
            foreach (string argItem in newArgs)
            {
                string item = argItem.Trim();
                if (item.Length == 0)
                    continue;
                int firstSpace = item.IndexOf(" ");
                string argKey = item.Substring(0, firstSpace);
                int valLength = firstSpace + 1;
                valLength = item.Length - valLength + 1;
                string argValue = item.Substring(firstSpace, valLength);

                if (!acceptableParams.Contains(argKey.ToLower()))
                    throw new ArgumentException(argKey + " is not an acceptable argument");

                dArgs.Add(argKey.Trim(), argValue.Trim());
            }

			if (dArgs.ContainsKey("m"))
			{
				if (dArgs["m"].ToUpper() == "RELEASE")
					isReleaseMode = true;
			}

			if (dArgs.ContainsKey("s"))
			{
				if (dArgs["s"].ToUpper() == "TRUE")
					getsource = true;
			}

			if (dArgs.ContainsKey("r"))
			{
				if (dArgs["r"].ToUpper() == "TRUE")
					useFileReferences = true;
			}


            string path = Environment.CurrentDirectory;
            DirectoryInfo currentDirectory = new DirectoryInfo(Environment.CurrentDirectory);

            StreamWriter SW;

            if (!dArgs.ContainsKey("n"))
                throw new ArgumentException("please make sure to include the '-n $(MSBuildProjectName)' parameter in the .csproj file");
            SW = File.CreateText(path + "\\" + dArgs["n"] + ".ddf");

            SW.WriteLine(".OPTION Explicit");
            SW.WriteLine(".Set DiskDirectoryTemplate=CDROM");
            SW.WriteLine(".Set CompressionType=MSZIP");
            SW.WriteLine(".Set UniqueFiles=Off");
            SW.WriteLine(".Set Cabinet=On");
            SW.WriteLine(".Set CabinetFileCountThreshold=0");
            SW.WriteLine(".Set FolderFileCountThreshold=0");
            SW.WriteLine(".Set FolderSizeThreshold=0");
            SW.WriteLine(".Set MaxCabinetSize=0");
            SW.WriteLine(".Set MaxDiskFileCount=0");
            SW.WriteLine(".Set MaxDiskSize=0");
            SW.WriteLine(";**************************************************");
            SW.WriteLine("");

			string assemblyName = dArgs["n"];

			if (!isReleaseMode)
			{
				SW.WriteLine("bin\\debug\\" + assemblyName + " " + assemblyName);
			}
			else
			{
				SW.WriteLine("bin\\release\\" + assemblyName + " " + assemblyName);
			}
                //SW.WriteLine("bin\\debug\\" + dArgs["n"] + ".pdb " + dArgs["n"] + ".pdb");
            //}
            //else
            //{
            //    SW.WriteLine("bin\\release\\" + dArgs["n"] + ".dll " + dArgs["n"] + ".dll");
            //}
            SW.WriteLine("manifest.xml");


            DirectoryInfo templateDirectory = new DirectoryInfo(path + "\\TEMPLATE");

            if (!templateDirectory.Exists)
                return;

			WalkDirsForAssemblies(SW, templateDirectory, assemblyName, isReleaseMode, useFileReferences);

            path = path + "\\";

			WalkSubDirs(SW, templateDirectory, path, false, getsource);

            if (dArgs.ContainsKey("d"))
            {
                string[] extraDirectories = dArgs["d"].Split('|');
                foreach (string extraDirectory in extraDirectories)
                {
                    if (extraDirectory.Trim().Length == 0)
                        continue;

                    DirectoryInfo extraDir = new DirectoryInfo(path + "\\" + extraDirectory);
                    if (extraDir.Exists)
                        WalkSubDirs(SW, extraDir, path, true);
                }
            }


            SW.Close();

            WriteManifestFile manifestWriter = new WriteManifestFile(assemblyName);
            manifestWriter.Path = path;
            //manifestWriter.IsDebug = !isReleaseMode;
            //manifestWriter.IncludePDB = includePDB;
            manifestWriter.Stream = SW;
            manifestWriter.TemplateFilename = "ManifestTemplate.xml";
            manifestWriter.CreateManifestFile();

        }

		private static void WalkDirsForAssemblies(StreamWriter sw, DirectoryInfo templatesDirectory, string assembly, bool isRelease)
		{
			WalkDirsForAssemblies(sw, templatesDirectory, assembly, false, false);
		}

        private static void WalkDirsForAssemblies(StreamWriter sw, DirectoryInfo templatesDirectory, string assembly)
        {
			WalkDirsForAssemblies(sw, templatesDirectory, assembly, false);
        }

		private static void WalkDirsForAssemblies(StreamWriter sw, DirectoryInfo templatesDirectory, string assembly, bool isRelease, bool moveFiles)
		{
			DirectoryInfo root = templatesDirectory.Parent;
			DirectoryInfo bin;
			if (!isRelease)
				bin = new DirectoryInfo(root.FullName + "\\bin\\debug");
			else
				bin = new DirectoryInfo(root.FullName + "\\bin\\release");

			DirectoryInfo assembliesDir = new DirectoryInfo(root.FullName + "\\Assemblies");

			foreach (FileInfo assemblyFile in bin.GetFiles("*.dll"))
			{
				if (assemblyFile.Name.ToLower() != assembly.ToLower())
				{
					if (assembliesDir.Exists)
					{
						assemblyFile.CopyTo(root.FullName + "\\Assemblies\\" + assemblyFile.Name, true);
						sw.WriteLine("Assemblies\\" + assemblyFile.Name + " " + assemblyFile.Name);
					}
				}
			}

			//DirectoryInfo assembliesDir = new DirectoryInfo(root.FullName + "\\Assemblies");
			//if (assembliesDir.Exists)
			//{
			//    FileInfo[] assemblies = assembliesDir.GetFiles("*.dll");
			//    foreach (FileInfo assembly in assemblies)
			//    {
			//        sw.WriteLine("Assemblies\\" + assembly.Name + " " + assembly.Name);//bin\\debug\\" + dArgs["n"] + ".dll " + dArgs["n"] + ".dll");
			//    }
			//}
		}

        static void WalkSubDirs(StreamWriter sw, DirectoryInfo directory, string path)
        {
            WalkSubDirs(sw, directory, path, false, false);
        }

		static void WalkSubDirs(StreamWriter sw, DirectoryInfo directory, string path, bool removeRootInDDF)
		{
			WalkSubDirs(sw, directory, path, removeRootInDDF, false);
		}

        static void WalkSubDirs(StreamWriter sw, DirectoryInfo directory, string path, bool removeRootInDDF, bool getSource)
        {
			if(!getSource)
				getSourceFiles(directory);

            List<string> excludedFileTypes = new List<string>();
            excludedFileTypes.Add(".CS");
            excludedFileTypes.Add(".SCC");
            excludedFileTypes.Add(".EXCLUDE");
			excludedFileTypes.Add(".SOURCE");
			excludedFileTypes.Add(".SPSOURCE");
			excludedFileTypes.Add(".PROVISIONHELPER");

            if (directory.Name.ToUpper().IndexOf(".EXCLUDE") != -1)
                return;

            FileInfo[] files = directory.GetFiles();
            if (files.Length > 0)
            {
                sw.WriteLine("");
                foreach (FileInfo file in files)
                {
                    if (excludedFileTypes.Contains(file.Extension.ToUpper()))
                        continue;

                    string fileFullName = file.FullName.Replace(path, "");
                    string writeFile = fileFullName.Replace("TEMPLATE\\", "");
                    string parentName = directory.Parent.Name;
                    if (parentName.ToUpper() == "FEATURES")
                        writeFile = writeFile.Replace(parentName + "\\", "");

                    if (directory.Name.ToUpper() == "RESOURCES")
                    {
                        writeFile = writeFile.Replace("FEATURES\\", "");
                        writeFile = writeFile.Replace("RESOURCES\\", "");
                        sw.WriteLine("\"" + fileFullName + "\" \"" + writeFile + "\"");
                        writeFile = "RESOURCES\\" + file.Name;
                    }

                    if (directory.Name.ToUpper() == "LISTTEMPLATES")
                    {
                        writeFile = writeFile.Replace("FEATURES\\", "");
                    }

                    if (removeRootInDDF)
                        writeFile = file.Name;

                    sw.WriteLine("\"" + fileFullName + "\" \"" + writeFile + "\"");
                }
            }

            DirectoryInfo[] dirs = directory.GetDirectories();
            foreach (DirectoryInfo dir in dirs)
            {
                WalkSubDirs(sw, dir, path, false, getSource);
            }
        }

		private static void getSourceFiles(DirectoryInfo directory)
		{
			FileInfo[] files = directory.GetFiles("*.source");
			foreach (FileInfo file in files)
			{
				//Debugger.Launch();
				XmlDocument source = new XmlDocument();
				source.PreserveWhitespace = true;
				source.Load(file.FullName);

				XmlElement siteElement = source.DocumentElement.SelectSingleNode("site") as XmlElement;
				XmlElement webElement = source.DocumentElement.SelectSingleNode("web") as XmlElement;
				XmlNodeList filesElement = source.DocumentElement.SelectNodes("//file");//.SelectSingleNode("files") as XmlElement;
				XmlNodeList contentTypeElements = source.DocumentElement.SelectNodes("//contentType");

				string siteUrl = siteElement.Attributes["url"].Value;
				string webUrl = webElement.Attributes["url"].Value;
				//string fileUrl = fileElement.Attributes["url"].Value;

				SPSecurity.RunWithElevatedPrivileges(delegate()
				{
					using (SPSite site = new SPSite(siteUrl))
					{
						using (SPWeb web = site.OpenWeb(webUrl))
						{
							//Debugger.Launch();
							getSPFiles(web, file);
							

							foreach (XmlElement contentTypeElement in contentTypeElements)
							{								
								writeSourceCT(web, contentTypeElement, directory);
							}
						}
					}
				});
			}
		}

		private static void getSPFiles(SPWeb web, FileInfo file)
		{
			//Debugger.Launch();

			XmlDocument source = new XmlDocument();
			source.PreserveWhitespace = true;
			source.Load(file.FullName);

			XmlNodeList filesElement = source.DocumentElement.SelectNodes("//file");//.SelectSingleNode("files") as XmlElement;

			XmlDocument xmlDocument = new XmlDocument();
			XmlNode rootNode = xmlDocument.CreateElement("Elements");
			xmlDocument.AppendChild(rootNode);

			XmlNode module = xmlDocument.CreateElement("Module");

			XmlAttribute moduleAttribute;
			XmlNode moduleAttributes = source.DocumentElement.SelectSingleNode("moduleAttributes");
			if (moduleAttributes != null)
			{
				foreach (XmlAttribute moduleAttrib in moduleAttributes.Attributes)
				{
					moduleAttribute = xmlDocument.CreateAttribute(moduleAttrib.Name);
					moduleAttribute.Value = moduleAttrib.Value;
					module.Attributes.Append(moduleAttribute);
				}
			}

			string siteUrl = string.Empty;

			using (SPSite site = web.Site)
			{
				siteUrl = site.Url;
			}

			foreach (XmlElement fileElement in filesElement)
			{
				string fileUrl = fileElement.Attributes["url"].Value;
				SPFile spFile = web.GetFile(fileUrl);
				
				byte[] fileBytes = spFile.OpenBinary(SPOpenBinaryOptions.SkipVirusScan);
				string newFilePath = file.DirectoryName + "\\" + spFile.Name;
				if (File.Exists(newFilePath))
					File.Delete(newFilePath);

				XmlNode fileNode = xmlDocument.CreateElement("File");

				XmlAttribute fileAttrib;

				fileAttrib = xmlDocument.CreateAttribute("Url");
				fileAttrib.Value = spFile.Name;
				fileNode.Attributes.Append(fileAttrib);

				fileAttrib = xmlDocument.CreateAttribute("Type");
				fileAttrib.Value = "GhostableInLibrary";
				fileNode.Attributes.Append(fileAttrib);

				fileAttrib = xmlDocument.CreateAttribute("IgnoreIfAlreadyExists");
				fileAttrib.Value = "TRUE";
				fileNode.Attributes.Append(fileAttrib);

				List<string> excludedProperties = new List<string>();
				excludedProperties.Add("METAINFO");
				excludedProperties.Add("ORDER");
				excludedProperties.Add("FILELEAFREF");

				SPListItem parentLI = spFile.Item;
				foreach (SPField field in parentLI.Fields)
				{
					try
					{
						//if it's a read only field, ignore it
						if (!field.ReadOnlyField)
						{
							string propName = field.StaticName;
							string propValue = string.Empty;

							if (excludedProperties.Contains(propName.ToUpper()))
								continue;

							if (parentLI[propName] != null)
								propValue = parentLI[propName].ToString();
							else
								continue;

							if (propValue.Contains(siteUrl))
							{
								propValue = propValue.Replace(siteUrl, "~SiteCollection");
							}

							string property = "<Property Name=\"" + propName + "\" Value=\"" + propValue + "\"/>";
							fileNode.InnerXml += property;
						}
					}
					catch { }
				}

				module.AppendChild(fileNode);				
				
				FileStream fsFile = File.Create(file.DirectoryName + "\\" + spFile.Name);
				fsFile.Write(fileBytes, 0, fileBytes.Length);
				fsFile.Close();
			}

			rootNode.AppendChild(module);

			XmlAttribute nameSpace = xmlDocument.CreateAttribute("xmlns");
			nameSpace.Value = "http://schemas.microsoft.com/sharepoint/";
			rootNode.Attributes.Append(nameSpace);

			XmlAttribute attribCreateProv = source.DocumentElement.Attributes["createProvisioningFile"];
			if (attribCreateProv != null && attribCreateProv.Value.ToUpper() == "TRUE")
			{
				string provFilePath = file.Directory.Parent.FullName + "\\" + file.Directory.Name + "_Provisioner.xml";
				if (File.Exists(provFilePath))
					File.Delete(provFilePath);

				xmlDocument.Save(provFilePath);
			}
		}

		private static void writeSourceCT(SPWeb web, XmlElement contentTypeElement, DirectoryInfo directory)
		{
			using (web)
			{
				XmlDocument ctDocument = new XmlDocument();
				XmlDocument columnDocument = new XmlDocument();

				XmlNode ctRootNode = ctDocument.CreateElement("Elements");
				XmlNode colRootNode = columnDocument.CreateElement("Elements");				
				ctDocument.AppendChild(ctRootNode);				
				columnDocument.AppendChild(colRootNode);

				XmlNode contentTypeNode;
				XmlAttribute contentTypeAttribute;

				contentTypeNode = ctDocument.CreateElement("ContentType");

				string ctName = contentTypeElement.Attributes["name"].Value;
				SPContentType sourceCT = web.ContentTypes[ctName];

				// ID
				contentTypeAttribute = ctDocument.CreateAttribute("ID");
				contentTypeAttribute.Value = sourceCT.Id.ToString();
				contentTypeNode.Attributes.Append(contentTypeAttribute);

				// name
				contentTypeAttribute = ctDocument.CreateAttribute("Name");
				contentTypeAttribute.Value = sourceCT.Name;
				contentTypeNode.Attributes.Append(contentTypeAttribute);

				// Description
				if (!string.IsNullOrEmpty(sourceCT.Description))
				{
					contentTypeAttribute = ctDocument.CreateAttribute("Description");
					contentTypeAttribute.Value = sourceCT.Description;
					contentTypeNode.Attributes.Append(contentTypeAttribute);
				}

				// group
				contentTypeAttribute = ctDocument.CreateAttribute("Group");
				contentTypeAttribute.Value = sourceCT.Group;
				contentTypeNode.Attributes.Append(contentTypeAttribute);

				// sealed
				if (sourceCT.Sealed)
				{
					contentTypeAttribute = ctDocument.CreateAttribute("Sealed");
					contentTypeAttribute.Value = sourceCT.Sealed.ToString();
					contentTypeNode.Attributes.Append(contentTypeAttribute);
				}

				// hidden
				if (sourceCT.Hidden)
				{
					contentTypeAttribute = ctDocument.CreateAttribute("Hidden");
					contentTypeAttribute.Value = sourceCT.Hidden.ToString();
					contentTypeNode.Attributes.Append(contentTypeAttribute);
				}

				// ReadOnly
				if (sourceCT.ReadOnly)
				{
					contentTypeAttribute = ctDocument.CreateAttribute("ReadOnly");
					contentTypeAttribute.Value = sourceCT.ReadOnly.ToString();
					contentTypeNode.Attributes.Append(contentTypeAttribute);
				}

				SPFieldLinkCollection fieldLinks = sourceCT.FieldLinks;

				SPContentType parentCT = sourceCT.Parent;

				XmlNode fieldRefs = ctDocument.CreateElement("FieldRefs");

				foreach (SPFieldLink sourceFieldLink in sourceCT.FieldLinks)
				{					
					string sourceSchema = string.Empty;

					if (parentCT.Fields.ContainsField(sourceFieldLink.Name))
						continue;

					sourceSchema = sourceFieldLink.SchemaXml;
					fieldRefs.InnerXml += sourceSchema;

					SPField field = sourceCT.Fields[sourceFieldLink.Id];
					string sourceFieldSchema = field.SchemaXml;
					colRootNode.InnerXml += sourceFieldSchema;
				}

				contentTypeNode.AppendChild(fieldRefs);
				ctRootNode.AppendChild(contentTypeNode);
				XmlNodeList fieldRefFields = ctDocument.SelectNodes("//FieldRef");
				foreach (XmlNode FieldRefField in fieldRefFields)
				{
					XmlAttribute sourceID = FieldRefField.Attributes["SourceID"];
					FieldRefField.Attributes.Remove(sourceID);//["SourceID"].

					XmlAttribute customization = FieldRefField.Attributes["Customization"];
					FieldRefField.Attributes.Remove(customization);//["SourceID"].

					XmlAttribute staticName = FieldRefField.Attributes["StaticName"];
					FieldRefField.Attributes.Remove(staticName);//["SourceID"].

					XmlAttribute Version = FieldRefField.Attributes["Version"];
					if (Version != null)
						FieldRefField.Attributes.Remove(Version);//["SourceID"].

					XmlAttribute PITarget = FieldRefField.Attributes["PITarget"];
					if (PITarget != null)
						FieldRefField.Attributes.Remove(PITarget);//["SourceID"].

					XmlAttribute PrimaryPITarget = FieldRefField.Attributes["PrimaryPITarget"];
					if (PrimaryPITarget != null)
						FieldRefField.Attributes.Remove(PrimaryPITarget);//["SourceID"].

					XmlAttribute PIAttribute = FieldRefField.Attributes["PIAttribute"];
					if (PIAttribute != null)
						FieldRefField.Attributes.Remove(PIAttribute);//["SourceID"].

					XmlAttribute PrimaryPIAttribute = FieldRefField.Attributes["PrimaryPIAttribute"];
					if (PrimaryPIAttribute != null)
						FieldRefField.Attributes.Remove(PrimaryPIAttribute);//["SourceID"].

					XmlAttribute Aggregation = FieldRefField.Attributes["Aggregation"];
					if (Aggregation != null)
						FieldRefField.Attributes.Remove(Aggregation);//["SourceID"].

					XmlAttribute Node = FieldRefField.Attributes["Node"];
					if (Node != null)
						FieldRefField.Attributes.Remove(Node);//["SourceID"].
				}


				XmlNodeList columnFields = columnDocument.SelectNodes("//Field");
				foreach (XmlNode columnField in columnFields)
				{
					XmlAttribute sourceID = columnField.Attributes["SourceID"];
					columnField.Attributes.Remove(sourceID);//["SourceID"].

					XmlAttribute customization = columnField.Attributes["Customization"];
					columnField.Attributes.Remove(customization);//["SourceID"].

					XmlAttribute staticName = columnField.Attributes["StaticName"];
					columnField.Attributes.Remove(staticName);//["SourceID"].

					XmlAttribute Version = columnField.Attributes["Version"];
					if(Version != null)
						columnField.Attributes.Remove(Version);//["SourceID"].

					XmlAttribute PITarget = columnField.Attributes["PITarget"];
					if (PITarget != null)
						columnField.Attributes.Remove(PITarget);//["SourceID"].

					XmlAttribute PrimaryPITarget = columnField.Attributes["PrimaryPITarget"];
					if (PrimaryPITarget != null)
						columnField.Attributes.Remove(PrimaryPITarget);//["SourceID"].

					XmlAttribute PIAttribute = columnField.Attributes["PIAttribute"];
					if (PIAttribute != null)
						columnField.Attributes.Remove(PIAttribute);//["SourceID"].

					XmlAttribute PrimaryPIAttribute = columnField.Attributes["PrimaryPIAttribute"];
					if (PrimaryPIAttribute != null)
						columnField.Attributes.Remove(PrimaryPIAttribute);//["SourceID"].

					XmlAttribute Aggregation = columnField.Attributes["Aggregation"];
					if (Aggregation != null)
						columnField.Attributes.Remove(Aggregation);//["SourceID"].

					XmlAttribute Node = columnField.Attributes["Node"];
					if (Node != null)
						columnField.Attributes.Remove(Node);//["SourceID"].
				}

				XmlAttribute ctNameSpace = ctDocument.CreateAttribute("xmlns");
				ctNameSpace.Value = "http://schemas.microsoft.com/sharepoint/";
				ctRootNode.Attributes.Append(ctNameSpace);

				XmlAttribute colNameSpace = columnDocument.CreateAttribute("xmlns");
				colNameSpace.Value = "http://schemas.microsoft.com/sharepoint/";
				colRootNode.Attributes.Append(colNameSpace);				

				ctDocument.Save(directory.FullName + "\\" + sourceCT.Name.Replace(" ", "") + ".xml");
				columnDocument.Save(directory.FullName + "\\" + sourceCT.Name.Replace(" ", "") + "_Columns.xml");
			}

			
		}

		
    }
}
